#include "StdAfx.h"
#include "SoftNotifier.h"

/*
	Autor	   : Carmelo Campione - 20 April 2008	
	Base Autor : John O'Byrne - 15 July 2002
*/

namespace cm {
	namespace Windows {

#define IDT_HIDDEN			0
#define IDT_APPEARING		1
#define IDT_WAITING			2
#define IDT_DISAPPEARING	3

CSoftNotifier::CSoftNotifier(CWindow* pWndNotify,UINT WMToNotify) 
	: m_pWndNotify(pWndNotify),m_WMToNotify(WMToNotify),
	m_bMouseIsOver(FALSE),
	m_CloseButtonStatus(0),
	m_hBitmapRegion(NULL),
	m_nBitmapHeight(0),m_nBitmapWidth(0),
	m_hCursor(NULL),
	m_crNormalTextColor(RGB(133, 146, 181)),m_crSelectedTextColor(RGB(10, 36, 106)),
	m_dwTimeToStay(0),m_dwShowEvents(0),m_dwHideEvents(0),
	m_nCurrentPosX(0),m_nCurrentPosY(0),m_nCurrentWidth(0),m_nCurrentHeight(0),
	m_nIncrementShow(0),m_nIncrementHide(0),
	m_nPlacement(SN_BOTTOM),m_nAnimStatus(IDT_HIDDEN)
{
	m_uTextFormat = DT_CENTER | DT_WORDBREAK | DT_END_ELLIPSIS; // Default Text format (see DrawText in the win32 API for the different values)
	m_hCursor = ::LoadCursor(NULL, MAKEINTRESOURCE(32649)); // System Hand cursor

	// If running on NT, timer precision is 10 ms, if not timer precision is 50 ms
	OSVERSIONINFO osvi;
	osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
	GetVersionEx(&osvi);
	if (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT)
		m_dwTimerPrecision = 10;
	else
		m_dwTimerPrecision = 50;

	m_TitleFont.CreatePointFont(80,_T("Arial"));

	SetTextDefaultFont(); // We use default GUI Font
}

CSoftNotifier::~CSoftNotifier(void)
{
}

void CSoftNotifier::SetTitle(LPCTSTR Title)
{
	m_Title = Title;
}

void CSoftNotifier::Show(LPCTSTR szCaption,const CRect& rcPlacement,int nPlacement,DWORD dwTimeToShow,DWORD dwTimeToStay,DWORD dwTimeToHide)
{
	m_strCaption   = szCaption;
	m_dwTimeToStay = dwTimeToStay;
	m_nPlacement   = nPlacement;

	UINT nBitmapSize = 0;
	if ((m_nPlacement == SN_BOTTOM) || (m_nPlacement == SN_TOP))
	{
		nBitmapSize = m_nBitmapHeight;
	}
	else 
	{
		nBitmapSize = m_nBitmapWidth;
	}

	// We calculate the pixel increment and the timer value for the showing animation
	if (dwTimeToShow > m_dwTimerPrecision)
	{
		UINT nEvents = min((dwTimeToShow / m_dwTimerPrecision), nBitmapSize);
		m_dwShowEvents = dwTimeToShow / nEvents;
		m_nIncrementShow = nBitmapSize / nEvents;
	}
	else
	{
		m_dwShowEvents = m_dwTimerPrecision;
		m_nIncrementShow = nBitmapSize;
	}

	// We calculate the pixel increment and the timer value for the hiding animation
	if (dwTimeToHide > m_dwTimerPrecision)
	{
		UINT nEvents = min((dwTimeToHide / m_dwTimerPrecision), nBitmapSize);
		m_dwHideEvents = dwTimeToHide / nEvents;
		m_nIncrementHide = nBitmapSize / nEvents;
	}
	else
	{
		m_dwShowEvents = m_dwTimerPrecision;
		m_nIncrementHide = nBitmapSize;
	}

	// Compute init values for the animation
	switch (m_nAnimStatus)
	{
		case IDT_HIDDEN:
		{
			if (m_nPlacement == SN_LEFT)
			{    
				m_nCurrentPosX = rcPlacement.left;
				m_nCurrentPosY = rcPlacement.bottom - m_nBitmapHeight;
				m_nCurrentWidth = 0;
				m_nCurrentHeight = m_nBitmapHeight;
			}
			else if (m_nPlacement == SN_RIGHT)
			{
				m_nCurrentPosX = rcPlacement.right;
				m_nCurrentPosY = rcPlacement.bottom - m_nBitmapHeight;
				m_nCurrentWidth = 0;
				m_nCurrentHeight = m_nBitmapHeight;
			}
			else if (m_nPlacement == SN_BOTTOM)
			{
				m_nCurrentPosX = rcPlacement.left;
				m_nCurrentPosY = rcPlacement.bottom;
				m_nCurrentWidth = m_nBitmapWidth;
				m_nCurrentHeight = 0;
			}
			else // if (m_nPlacement==SN_TOP)
			{
				// Taskbar is on the bottom or Invisible
				m_nCurrentPosX = rcPlacement.right - m_nBitmapWidth;
				m_nCurrentPosY = rcPlacement.top;
				m_nCurrentWidth = m_nBitmapWidth;
				m_nCurrentHeight = 0;
			}
			ShowWindow(SW_SHOWNOACTIVATE);	
			SetTimer(IDT_APPEARING, m_dwShowEvents, NULL);
			break;
		}
		case IDT_APPEARING:
		{
			RedrawWindow();
			break;
		}
		case IDT_WAITING:
		{
			RedrawWindow();
			KillTimer(IDT_WAITING);
			SetTimer(IDT_WAITING, m_dwTimeToStay, NULL);
			break;
		}
		case IDT_DISAPPEARING:
		{
			KillTimer(IDT_DISAPPEARING);
			SetTimer(IDT_WAITING, m_dwTimeToStay, NULL);
			if (m_nPlacement == SN_LEFT)
			{
				m_nCurrentPosX = rcPlacement.left - m_nBitmapWidth;
				m_nCurrentWidth = m_nBitmapWidth;
			}
			else if (m_nPlacement == SN_RIGHT)
			{
				m_nCurrentPosX = rcPlacement.right;
				m_nCurrentWidth = m_nBitmapWidth;
			}
			else if (m_nPlacement == SN_BOTTOM)
			{
				m_nCurrentPosY = rcPlacement.bottom;
				m_nCurrentHeight = m_nBitmapHeight;
			}
			else // if (m_nPlacement==SN_TOP)
			{
				m_nCurrentPosY = rcPlacement.top - m_nBitmapHeight;
				m_nCurrentHeight = m_nBitmapHeight;
			}
			SetWindowPos(HWND_TOP, m_nCurrentPosX, m_nCurrentPosY, m_nCurrentWidth, m_nCurrentHeight, SWP_NOACTIVATE);
			RedrawWindow();
			break;
		}
	}
}

void CSoftNotifier::Hide()
{
	switch (m_nAnimStatus)
	{
	case IDT_APPEARING:
		KillTimer(IDT_APPEARING);
		break;
	case IDT_WAITING:
		KillTimer(IDT_WAITING);
		break;
	case IDT_DISAPPEARING:
		KillTimer(IDT_DISAPPEARING);
		break;
	}
	MoveWindow(0, 0, 0, 0);
	ShowWindow(SW_HIDE);
	m_nAnimStatus = IDT_HIDDEN;
}

HRGN CSoftNotifier::CreateRgnFromBitmap(HBITMAP hBmp, COLORREF color)
{
	// this code is written by Davide Pizzolato

	if (!hBmp)
		return NULL;

	BITMAP bm;
	GetObject(hBmp, sizeof(BITMAP), &bm);	// get bitmap attributes

	CDC dcBmp;
	dcBmp.CreateCompatibleDC(GetDC());	// Creates a memory device context for the bitmap
	dcBmp.SelectBitmap(hBmp);			// selects the bitmap in the device context

	const DWORD RDHDR = sizeof(RGNDATAHEADER);
	const DWORD MAXBUF = 40;		// size of one block in RECTs
	// (i.e. MAXBUF*sizeof(RECT) in bytes)
	LPRECT	pRects;								
	DWORD	cBlocks = 0;			// number of allocated blocks

	INT		i, j;					// current position in mask image
	INT		first = 0;				// left position of current scan line
	// where mask was found
	bool	wasfirst = false;		// set when if mask was found in current scan line
	bool	ismask;					// set when current color is mask color

	// allocate memory for region data
	RGNDATAHEADER* pRgnData =(RGNDATAHEADER*)new BYTE[RDHDR + ++cBlocks * MAXBUF * sizeof(RECT)];
	memset(pRgnData, 0, RDHDR + cBlocks * MAXBUF * sizeof(RECT));
	// fill it by default
	pRgnData->dwSize	= RDHDR;
	pRgnData->iType		= RDH_RECTANGLES;
	pRgnData->nCount	= 0;
	for (i = 0; i < bm.bmHeight; i++)
	{
		for (j = 0; j < bm.bmWidth; j++)
		{
			// get color
			ismask = (dcBmp.GetPixel(j, bm.bmHeight - i - 1) != color);
			// place part of scan line as RECT region if transparent color found after mask color or
			// mask color found at the end of mask image
			if (wasfirst &&((ismask &&(j == (bm.bmWidth - 1))) || (ismask ^(j < bm.bmWidth))))
			{
				// get offset to RECT array if RGNDATA buffer
				pRects =(LPRECT)((LPBYTE)pRgnData + RDHDR);
				// save current RECT
				pRects[pRgnData->nCount++] = CRect(first, bm.bmHeight - i - 1, j + (j == (bm.bmWidth - 1)), bm.bmHeight - i);
				// if buffer full reallocate it
				if (pRgnData->nCount >= cBlocks * MAXBUF)
				{
					LPBYTE pRgnDataNew = new BYTE[RDHDR + ++cBlocks * MAXBUF * sizeof(RECT)];
					memcpy(pRgnDataNew, pRgnData, RDHDR +(cBlocks - 1) * MAXBUF * sizeof(RECT));
					delete pRgnData;
					pRgnData =(RGNDATAHEADER*)pRgnDataNew;
				}
				wasfirst = false;
			} 
			else if (!wasfirst && ismask)
			{
				// set wasfirst when mask is found
				first = j;
				wasfirst = true;
			}
		}
	}
	dcBmp.DeleteDC();	// release the bitmap
	// create region
	/*  Under WinNT the ExtCreateRegion returns NULL(by Fable@aramszu.net) */
	//	HRGN hRgn = ExtCreateRegion( NULL, RDHDR + pRgnData->nCount * sizeof(RECT), (LPRGNDATA)pRgnData );
	/* ExtCreateRegion replacement 
	{
	*/
	HRGN hRgn = CreateRectRgn(0, 0, 0, 0);
	assert(hRgn != NULL);
	pRects =(LPRECT)((LPBYTE)pRgnData + RDHDR);
	for (i = 0; i < (int)pRgnData->nCount; i++)
	{
		HRGN hr = CreateRectRgn(pRects[i].left, pRects[i].top, pRects[i].right, pRects[i].bottom);
		//VERIFY(CombineRgn(hRgn, hRgn, hr, RGN_OR) != ERROR);
		CombineRgn(hRgn, hRgn, hr, RGN_OR);
		if (hr)
			DeleteObject(hr);
	}
	assert(hRgn != NULL);
	/* 
	} ExtCreateRegion replacement */

	delete pRgnData;
	return hRgn;
}

BOOL CSoftNotifier::SetBitmap(UINT nBitmapID,short red,short green,short blue)
{
	BITMAP bm;

	if (m_bitmapBackground != NULL)
		m_bitmapBackground.DeleteObject();

	if (!m_bitmapBackground.LoadBitmap(nBitmapID))
		return FALSE;

	GetObject(m_bitmapBackground, sizeof(bm), &bm);
	m_nBitmapWidth	= bm.bmWidth;
	m_nBitmapHeight = bm.bmHeight;
	m_rcText.SetRect(0, 0, bm.bmWidth, bm.bmHeight);
	if (m_CloseButtonBitmap != NULL)
	{
		SIZE sz;
		m_CloseButtonBitmap.GetSize(sz);
		m_CloseButtonBitmapRect.SetRect(m_nBitmapWidth - 2 - sz.cx / 3,2,m_nBitmapWidth - 2, 2 + sz.cy);
		m_rcText.InflateRect(0,-(2 + sz.cy + 2));
	}
	if (red != -1 && green != -1 && blue != -1)
	{
		// No need to delete the HRGN,  SetWindowRgn() owns it after being called

		//m_hBitmapRegion = CreateRgnFromBitmap((HBITMAP)m_bitmapBackground.GetSafeHandle(), RGB(red, green, blue));
		m_hBitmapRegion = CreateRgnFromBitmap(m_bitmapBackground, RGB(red, green, blue));

		SetWindowRgn(m_hBitmapRegion, true);
	}

	return TRUE;
}

BOOL CSoftNotifier::SetBitmap(LPCTSTR szFileName,short red,short green,short blue)
{
	BITMAP bm;
	HBITMAP hBmp;

	hBmp = (HBITMAP)::LoadImage(ModuleHelper::GetResourceInstance(), szFileName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
	if (!hBmp)
		return FALSE;

	if (m_bitmapBackground != NULL)
		m_bitmapBackground.DeleteObject();

	m_bitmapBackground.Attach(hBmp);

	GetObject(m_bitmapBackground, sizeof(bm), &bm);
	m_nBitmapWidth	= bm.bmWidth;
	m_nBitmapHeight = bm.bmHeight;
	m_rcText.SetRect(0, 0, bm.bmWidth, bm.bmHeight);
	if (m_CloseButtonBitmap != NULL)
	{
		SIZE sz;
		m_CloseButtonBitmap.GetSize(sz);
		m_CloseButtonBitmapRect.SetRect(m_nBitmapWidth - 2 - sz.cx / 3,2,m_nBitmapWidth - 2, 2 + sz.cy);
		m_rcText.InflateRect(0,-(2 + sz.cy + 2));
	}
	if (red!=-1 && green!=-1 && blue!=-1)
	{
		// No need to delete the HRGN,  SetWindowRgn() owns it after being called

		//m_hBitmapRegion = CreateRgnFromBitmap((HBITMAP)m_bitmapBackground.GetSafeHandle(), RGB(red, green, blue));
		m_hBitmapRegion = CreateRgnFromBitmap(m_bitmapBackground, RGB(red, green, blue));

		SetWindowRgn(m_hBitmapRegion, true);
	}

	return TRUE;
}

BOOL CSoftNotifier::SetBitmapCloseButton(UINT nBitmapID)
{
	if (m_CloseButtonBitmap != NULL)
		m_CloseButtonBitmap.DeleteObject();

	if (!m_CloseButtonBitmap.LoadBitmap(nBitmapID))
		return FALSE;

	SIZE sz;
	m_CloseButtonBitmap.GetSize(sz);
	m_CloseButtonBitmapRect.SetRect(m_nBitmapWidth - 2 - sz.cx / 3,2,m_nBitmapWidth - 2, 2 + sz.cy);
	m_rcText.InflateRect(0,-(2 + sz.cy + 2));

	return TRUE;
}

void CSoftNotifier::SetTextFont(LPCTSTR szFont, int nSize, int nNormalStyle, int nSelectedStyle)
{
	LOGFONT lf;
	if (m_myNormalFont != NULL)
		m_myNormalFont.DeleteObject();
	m_myNormalFont.CreatePointFont(nSize, szFont);
	m_myNormalFont.GetLogFont(&lf);

	// We  set the Font of the unselected ITEM
	if (nNormalStyle & SN_TEXT_BOLD)
		lf.lfWeight = FW_BOLD;
	else
		lf.lfWeight = FW_NORMAL;

	if (nNormalStyle & SN_TEXT_ITALIC)
		lf.lfItalic = TRUE;
	else
		lf.lfItalic = FALSE;

	if (nNormalStyle & SN_TEXT_UNDERLINE)
		lf.lfUnderline = TRUE;
	else
		lf.lfUnderline = FALSE;

	m_myNormalFont.DeleteObject();
	m_myNormalFont.CreateFontIndirect(&lf);

	// We set the Font of the selected ITEM
	if (nSelectedStyle & SN_TEXT_BOLD)
		lf.lfWeight = FW_BOLD;
	else
		lf.lfWeight = FW_NORMAL;

	if (nSelectedStyle & SN_TEXT_ITALIC)
		lf.lfItalic = TRUE;
	else
		lf.lfItalic = FALSE;

	if (nSelectedStyle & SN_TEXT_UNDERLINE)
		lf.lfUnderline = TRUE;
	else
		lf.lfUnderline = FALSE;

	if (m_mySelectedFont != NULL)
		m_mySelectedFont.DeleteObject();
	m_mySelectedFont.CreateFontIndirect(&lf);
}

void CSoftNotifier::SetTextDefaultFont()
{
	LOGFONT lf;
	CFontHandle Font((HFONT)GetStockObject(DEFAULT_GUI_FONT));
	Font.GetLogFont(&lf);
	if (m_myNormalFont != NULL)
		m_myNormalFont.DeleteObject();
	m_myNormalFont.CreateFontIndirect(&lf);

	if (m_mySelectedFont != NULL)
		m_mySelectedFont.DeleteObject();
	lf.lfUnderline = TRUE;
	m_mySelectedFont.CreateFontIndirect(&lf);
}

void CSoftNotifier::SetTextColor(COLORREF crNormalTextColor, COLORREF crSelectedTextColor)
{
	m_crNormalTextColor	  = crNormalTextColor;
	m_crSelectedTextColor = crSelectedTextColor;
	RedrawWindow();
}

void CSoftNotifier::SetTextRect(RECT rcText)
{
	m_rcText = rcText;
}

void CSoftNotifier::SetTextFormat(UINT uTextFormat)
{
	m_uTextFormat = uTextFormat;
}

LRESULT CSoftNotifier::OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
	SetLayeredWindowAttributes(m_hWnd, 0, (255 * 70) / 100, LWA_ALPHA);
	return FALSE;
}

LRESULT CSoftNotifier::OnMouseMove(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
{
	TRACKMOUSEEVENT t_MouseEvent;
	t_MouseEvent.cbSize      = sizeof(TRACKMOUSEEVENT);
	t_MouseEvent.dwFlags     = TME_LEAVE | TME_HOVER;
	t_MouseEvent.hwndTrack   = m_hWnd;
	t_MouseEvent.dwHoverTime = 1;

	// We Tell Windows we want to receive WM_MOUSEHOVER and WM_MOUSELEAVE
	::_TrackMouseEvent(&t_MouseEvent);

	BYTE CurrentCloseButtonStatus = 0;// normal;
	
	LONG xPos = GET_X_LPARAM(lParam); 
	LONG yPos = GET_Y_LPARAM(lParam);
	if (m_CloseButtonBitmapRect.PtInRect(CPoint(xPos,yPos)) == TRUE)
	{
		CurrentCloseButtonStatus = 1;//over
		if (wParam & MK_LBUTTON)
		{
			CurrentCloseButtonStatus = 2;//over and pressed
		}
	}
	if (m_CloseButtonStatus != CurrentCloseButtonStatus)
	{
		m_CloseButtonStatus = CurrentCloseButtonStatus;
		RedrawWindow();
	}

	return 0;
}

LRESULT CSoftNotifier::OnLButtonUp(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
{
	LONG xPos = GET_X_LPARAM(lParam); 
	LONG yPos = GET_Y_LPARAM(lParam);
	if (m_CloseButtonBitmapRect.PtInRect(CPoint(xPos,yPos)) == TRUE)
	{
		Hide();
		return 0;
	}

	GetParent().PostMessage(m_WMToNotify);

	return 0;
}

LRESULT CSoftNotifier::OnMouseHover(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
{
	if (m_bMouseIsOver == FALSE)
	{
		m_bMouseIsOver = TRUE;
		SetWindowLong(GWL_EXSTYLE,GetWindowLong(GWL_EXSTYLE) & ~WS_EX_LAYERED);

		RedrawWindow();
	}

	return 0;
}

LRESULT CSoftNotifier::OnMouseLeave(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
	if (m_bMouseIsOver == TRUE)
	{
		m_bMouseIsOver = FALSE;

		m_CloseButtonStatus = 0; // normal;

		SetWindowLong(GWL_EXSTYLE,GetWindowLong(GWL_EXSTYLE) | WS_EX_LAYERED);
		SetLayeredWindowAttributes(m_hWnd,0,(255 * 70) / 100,LWA_ALPHA);

		RedrawWindow();
	}

	return 0;
}

LRESULT CSoftNotifier::OnEraseBkgnd(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
	CDCHandle		cDC((HDC)wParam);
	CDC				memDC;
	memDC.CreateCompatibleDC(cDC);
	CBitmapHandle OldBitmap = memDC.SelectBitmap(m_bitmapBackground);
	cDC.BitBlt(0, 0, m_nCurrentWidth, m_nCurrentHeight, memDC, 0, 0, SRCCOPY);
	memDC.SelectBitmap(OldBitmap);

	return TRUE;
}

LRESULT CSoftNotifier::OnPaint(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
	CPaintDC	dc(*this);
	CFontHandle OldFont;

	int OldBk = dc.SetBkMode(TRANSPARENT);

	OldFont = dc.SelectFont(m_TitleFont);

	dc.TextOut(1,1,m_Title,-1);

	dc.SelectFont(OldFont);

	if (m_bMouseIsOver)
	{
		dc.SetTextColor(m_crSelectedTextColor);
		OldFont = dc.SelectFont(m_mySelectedFont);
	}
	else
	{
		dc.SetTextColor(m_crNormalTextColor);
		OldFont = dc.SelectFont(m_myNormalFont);
	}
	
	CRect Dummy(m_rcText);
	Dummy.InflateRect(0,-5);

	dc.DrawText(m_strCaption, -1, Dummy, m_uTextFormat);

	dc.SelectFont(OldFont);

	if (m_CloseButtonBitmap != NULL)
	{
		CDC	memDC;
		memDC.CreateCompatibleDC(dc);
		CBitmapHandle OldBitmap = memDC.SelectBitmap(m_CloseButtonBitmap);
		dc.StretchBlt(m_CloseButtonBitmapRect.left,m_CloseButtonBitmapRect.top,
				m_CloseButtonBitmapRect.Width(),m_CloseButtonBitmapRect.Height(), 
					memDC, 
					m_CloseButtonStatus * m_CloseButtonBitmapRect.Width(), 0, 
						m_CloseButtonBitmapRect.Width(), m_CloseButtonBitmapRect.Height(), 
							SRCCOPY);
		memDC.SelectBitmap(OldBitmap);
	}

	dc.SetBkMode(OldBk);

	return 0;
}

LRESULT CSoftNotifier::OnSetCursor(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)
{
	UINT nHitTest = LOWORD(lParam);
	if (nHitTest == HTCLIENT)
	{
		SetCursor(m_hCursor);
		return TRUE;
	}

	return FALSE;
}

LRESULT CSoftNotifier::OnTimer(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
	UINT nIDEvent = wParam;
	switch (nIDEvent)
	{
	case IDT_APPEARING:
		{
			m_nAnimStatus = IDT_APPEARING;
			switch (m_nPlacement)
			{
			case SN_TOP:
				{
					if (m_nCurrentHeight + m_nIncrementShow <= m_nBitmapHeight)
					{
						m_nCurrentPosY -= m_nIncrementShow;
						m_nCurrentHeight += m_nIncrementShow;
					}
					else
					{
						m_nCurrentHeight = m_nBitmapHeight;
						KillTimer(IDT_APPEARING);
						SetTimer(IDT_WAITING, m_dwTimeToStay, NULL);
						m_nAnimStatus = IDT_WAITING;
					}
					break;
				}
			case SN_BOTTOM:
				{
					if (m_nCurrentHeight + m_nIncrementShow <= m_nBitmapHeight)
					{
						m_nCurrentHeight += m_nIncrementShow;
					}
					else
					{
						m_nCurrentHeight = m_nBitmapHeight;
						KillTimer(IDT_APPEARING);
						SetTimer(IDT_WAITING, m_dwTimeToStay, NULL);
						m_nAnimStatus = IDT_WAITING;
					}
					break;
				}
			case SN_RIGHT:
				{
					if (m_nCurrentWidth + m_nIncrementShow < m_nBitmapWidth)
					{
						m_nCurrentWidth += m_nIncrementShow;
					}
					else
					{
						m_nCurrentWidth = m_nBitmapWidth;
						KillTimer(IDT_APPEARING);
						SetTimer(IDT_WAITING, m_dwTimeToStay, NULL);
						m_nAnimStatus = IDT_WAITING;
					}
					break;
				}
			case SN_LEFT:
				{
					if (m_nCurrentWidth + m_nIncrementShow <= m_nBitmapWidth)
					{
						m_nCurrentPosX -= m_nIncrementShow;
						m_nCurrentWidth += m_nIncrementShow;
					}
					else
					{
						m_nCurrentWidth = m_nBitmapWidth;
						KillTimer(IDT_APPEARING);
						SetTimer(IDT_WAITING, m_dwTimeToStay, NULL);
						m_nAnimStatus = IDT_WAITING;
					}
					break;
				}
			}
			SetWindowPos(HWND_TOP, m_nCurrentPosX, m_nCurrentPosY, m_nCurrentWidth, m_nCurrentHeight, SWP_NOACTIVATE);
			break;
		}
	case IDT_WAITING:
		{
			if (m_bMouseIsOver == FALSE)
			{
				KillTimer(IDT_WAITING);
				SetTimer(IDT_DISAPPEARING, m_dwHideEvents, NULL);
			}
			break;
		}
	case IDT_DISAPPEARING:
		{
			m_nAnimStatus = IDT_DISAPPEARING;
			switch (m_nPlacement)
			{
			case SN_TOP:
				{
					if (m_nCurrentHeight>0)
					{
						m_nCurrentPosY += m_nIncrementHide;
						m_nCurrentHeight -= m_nIncrementHide;
					}
					else
					{
						KillTimer(IDT_DISAPPEARING);
						Hide();
					}
					break;
				}
			case SN_BOTTOM:
				{
					if (m_nCurrentHeight>0)
					{
						m_nCurrentHeight -= m_nIncrementHide;
					}
					else
					{
						KillTimer(IDT_DISAPPEARING);
						Hide();
					}
					break;
				}
			case SN_RIGHT:
				{
					if (m_nCurrentWidth>0)
					{
						m_nCurrentWidth -= m_nIncrementHide;
					}
					else
					{
						KillTimer(IDT_DISAPPEARING);
						Hide();
					}
					break;
				}
			case SN_LEFT:
				{
					if (m_nCurrentWidth>0)
					{
						m_nCurrentPosX += m_nIncrementHide;
						m_nCurrentWidth -= m_nIncrementHide;
					}
					else
					{
						KillTimer(IDT_DISAPPEARING);
						Hide();
					}
					break;
				}
			}
			SetWindowPos(HWND_TOP, m_nCurrentPosX, m_nCurrentPosY, m_nCurrentWidth, m_nCurrentHeight, SWP_NOACTIVATE);
			break;
		}
	}
	return 0;
}
}}
